package runner

import (
	"fmt"
	"gitee.com/gitee-go/core"
	"gitee.com/gitee-go/core/runtime"
	"gitee.com/gitee-go/runner-core/runCloud"
	"gitee.com/gitee-go/utils"
	"gitee.com/gitee-go/utils/ioex"
	hbtp "github.com/mgr9525/HyperByte-Transfer-Protocol"
	"io"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
)

// 根据类型获取制品
func (c *ExecTask) getdepnarti() (map[string]string, error) {
	if len(c.job.DependArtifacts) <= 0 {
		return nil, nil
	}
	envs := c.getdepnevns() // 获取所有dependOn的最终变量
	rtmp := make(map[string]string)
	for _, v := range c.job.DependArtifacts {
		switch v.Type {
		case "archive", "pipeline":
			if err := c.getArtifactPipeline(v); err != nil {
				core.Log.Errorf("getArtifactPipeline err:%v", err)
				return nil, fmt.Errorf("getArtifactPipeline err:%v", err)
			}
		case "variable":
			rtmp[v.Name] = envs[v.Name] //如果key存在就有值,不然就是""空值
		default:
			return nil, fmt.Errorf("dependArtifact(%s) type err:%s", v.Name, v.Type)
		}
	}

	return rtmp, nil
}

// 获取所有dependOn的最终变量
func (c *ExecTask) getdepnevns() map[string]string {
	rt := make(map[string]string)
	if len(c.job.DependsOn) <= 0 {
		return rt
	}

	for _, v := range c.job.DependsOn {
		if err := c.getArtifactEnv(rt, v); err != nil {
			core.Log.Errorf("getArtifactEnv err(%s):%v", v, err)
		}
	}
	return rt
}

// 调用server查找指定制品,存在就下载
// 1. 判断参数
// 2. 调用server函数
// 3. 成功就把连接转为长连接,下载文件
func (c *ExecTask) getArtifactPipeline(art *runtime.DependArtifact) (rterr error) {
	defer func() {
		core.Log.Debugf("getArtifactPipeline end %s(%s)", art.Name, art.Id)
		if err := recover(); err != nil {
			core.LogPnc.Errorf("ExecTask getArtifactPipeline:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
			rterr = fmt.Errorf("recover err:%v", err)
		}
	}()

	core.Log.Debugf("getArtifactPipeline start %s(%s)", art.Name, art.Id)
	target := strings.TrimSpace(art.Target)
	/*if target==""||target=="."||target=="./"||target==".\\"{
		target=c.repopth
	}*/
	if !strings.HasPrefix(target, "/") {
		target = filepath.Join(c.repopth, target)
	}

	stat, err := os.Stat(target)
	if err == nil {
		if !art.IsForce {
			return fmt.Errorf("already exist:%s", target)
		}
		if !stat.IsDir() {
			return fmt.Errorf("artifact (%s) err:%s is not dir", art.Name, target)
		}
	} else {
		os.MkdirAll(target, 0755)
	}

	req, err := runCloud.NewReq("GetArtPipe")
	if err != nil {
		return err
	}
	defer req.Close()
	err = req.Do(nil, utils.Map{
		"buildid":   c.job.BuildId,
		"jobid":     c.job.Id,
		"art_type":  art.Type,
		"art_name":  art.Name,
		"art_stage": art.SourceStage,
		"art_job":   art.SourceJob,
		"art_repo":  art.Repository,
	})
	if err != nil {
		return err
	}
	if req.ResCode() != hbtp.ResStatusOk {
		return fmt.Errorf(string(req.ResBodyBytes()))
	}
	sz := ioex.BigByteToInt(req.ResBodyBytes())
	if sz <= 0 {
		return fmt.Errorf("down artifact size err:%d", sz)
	}
	flpth := filepath.Join(c.wrkpth, "tmp.art")
	os.RemoveAll(flpth)
	defer os.RemoveAll(flpth)
	zipfl, err := os.OpenFile(flpth, os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer zipfl.Close()
	defer os.RemoveAll(flpth)
	bts := make([]byte, 1024)
	rn := int64(0)
	for {
		n, err := req.Conn().Read(bts)
		if n > 0 {
			rn += int64(n)
			zipfl.Write(bts[:n])
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
	}
	zipfl.Close()
	if rn != sz {
		return fmt.Errorf("get artifact file size err:%d/%d", rn, sz)
	}
	return ioex.UnZip(flpth, target)
}

// 调用server获取制品变量
func (c *ExecTask) getArtifactEnv(mp map[string]string, jobnm string) (rterr error) {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("ExecTask getArtifact:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
			rterr = fmt.Errorf("recover err:%v", err)
		}
	}()

	bdmp := make(map[string]string)
	err := runCloud.DoJson("GetArtEnvs", utils.Map{
		"buildid": c.job.BuildId,
		"jobid":   c.job.Id,
		"stageid": c.job.StageId,
		"jobnm":   jobnm,
	}, &bdmp)
	if err != nil {
		return err
	}

	for k, v := range bdmp {
		mp[k] = v
	}
	return nil
}

// ------------------------------------------------------------------------------------------------
// 生成制品,根据制品描述查找文件,上传
func (c *ExecTask) genArtifact() (rterr error) {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("ExecTask genArtifact:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
			rterr = fmt.Errorf("genArtifact err:%v", err)
		}
	}()

	if len(c.job.Artifacts) <= 0 {
		return nil
	}

	varkeys := make(map[string]bool, 0)
	for _, art := range c.job.Artifacts {
		switch art.Scope {
		case "archive", "pipeline":
			if err := c.genArtifactPipeline(art); err != nil {
				core.Log.Errorf("genArtifactPipeline err:%v", err)
				return fmt.Errorf("genArtifactPipeline err:%v", err)
			}
		case "variable":
			if art.Name != "" {
				varkeys[art.Name] = true
			}
		default:
			return fmt.Errorf("Artifact(%s) scope err:%s", art.Name, art.Scope)
		}
	}

	if err := c.genArtifactVar(varkeys); err != nil {
		return fmt.Errorf("genArtifactVar err:%v", err)
	}

	return nil
}

// 生成文件类型的制品
func (c *ExecTask) genArtifactPipeline(art *runtime.Artifact) (rterr error) {
	defer func() {
		core.Log.Debugf("genArtifactPipeline end %s(%s)", art.Name, art.Id)
		if err := recover(); err != nil {
			core.LogPnc.Errorf("ExecTask genArtifact:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
			rterr = fmt.Errorf("genArtifact err:%v", err)
		}
	}()
	core.Log.Debugf("genArtifactPipeline start %s(%s)", art.Name, art.Id)
	src := filepath.Join(c.repopth, art.Path)
	if !ioex.PathExists(src) {
		return fmt.Errorf("not found artifact:%s", art.Path)
	}
	dst := filepath.Join(c.wrkpth, fmt.Sprintf("%s.art", art.Id))
	err := ioex.Zip(src, dst)
	if err != nil {
		return err
	}

	zipst, err := os.Stat(dst)
	if err != nil {
		return err
	}
	zipfl, err := os.Open(dst)
	if err != nil {
		return err
	}
	defer zipfl.Close()

	req, err := runCloud.NewReq("UploadArtPipe")
	if err != nil {
		return err
	}
	defer req.Close()
	err = req.Do(nil, utils.Map{
		"buildid":  c.job.BuildId,
		"jobid":    c.job.Id,
		"artid":    art.Id,
		"art_size": zipst.Size(),
	})
	if err != nil {
		return err
	}
	if req.ResCode() != hbtp.ResStatusOk {
		return fmt.Errorf("server err(artifact:%s-%s):%s", art.Scope, art.Name, string(req.ResBodyBytes()))
	}

	bts := make([]byte, 1024*5)
	for !ioex.CheckContext(c.cmdctx) {
		n, err := zipfl.Read(bts)
		if n > 0 {
			_, err := req.Conn().Write(bts[:n])
			if err != nil {
				return fmt.Errorf("upload err:%v", err)
			}
		}
		if err != nil {
			break
		}
	}
	/*err=req.Res()
	if err != nil {
		return err
	}*/

	return nil
}

// 生成变量制品并转成map上传到server
func (c *ExecTask) genArtifactVar(varkeys map[string]bool) (rterr error) {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("ExecTask genArtifact:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
			rterr = fmt.Errorf("recover err:%v", err)
		}
	}()

	if len(varkeys) <= 0 || len(c.cmdenv) <= 0 {
		return nil
	}
	// 注入环境变量
	mps := make(map[string]string)
	c.cmdenvlk.RLock()
	for _, v := range c.cmdenv {
		i := strings.Index(v, "=")
		if i > 0 {
			k := v[:i]
			val := v[i+1:]
			ok := varkeys[k]
			if ok && val != "" {
				mps[k] = val
			}
		}
	}
	c.cmdenvlk.RUnlock()

	pars := utils.Map{
		"buildid": c.job.BuildId,
		"jobid":   c.job.Id,
	}
	pars["vars"] = mps
	code, bts, err := runCloud.DoString("UploadArtVar", pars)
	if err != nil {
		return err
	}
	if code != hbtp.ResStatusOk {
		return fmt.Errorf("server err(%d):%s", code, string(bts))
	}
	return nil
}
